/**
 * Copyright (c) 2011 Nokia Corporation.
 */

#include <QApplication>
#include <math.h>
#include <QTimer>
#include <QApplication>
#include <math.h>
#include <QDebug>
#include "mygamewindow.h"
#include "QtOpenGLSpriteBatch.h"
#include <QTouchEvent>

#ifdef LITE_VERSION
#include <QUrl>
#include <QDesktopServices>
#include "storelauncher.h"
#endif



/*!
  Constructor.
  Just zero everything. The initialization is done later at initializeGL
*/
MyGameWindow::MyGameWindow(QWidget *parent /* = 0 */)
    : QGLWidget(parent),
      spriteBatch(0)
{
#ifdef LITE_VERSION
    storeLauncher = 0;
#endif
    paused = true;
    appExiting = false;
    srand( QTime::currentTime().msec() + QTime::currentTime().second()  );
    theGame = 0;
    setAutoFillBackground( false );
    setAttribute (Qt::WA_OpaquePaintEvent);
    setAttribute( Qt::WA_NoSystemBackground );
    setAttribute( Qt::WA_NativeWindow );
#ifdef MS_USE_TOUCH_EVENTS
    setAttribute( Qt::WA_AcceptTouchEvents );
    for (int f=0; f<MAX_TOUCH_POINTS; f++) {
        touchEnabled[f] = false;
        exTouchEnabled[f] = false;
    }
    lastMultitouchDistance = 0.0f;
    lastTouchCount = 0;
#endif
    setAttribute( Qt::WA_PaintOnScreen, true );
    setAttribute( Qt::WA_StyledBackground,false);
    setAutoBufferSwap(false);
    // Startup the timer which will call updateGL as fast as it can.
    timer = new QTimer(this);
    QObject::connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
    //timer->start();
    // will be activated on first activate-event

}


/*!
  Destructor.
*/
MyGameWindow::~MyGameWindow()
{
    if (theGame) delete theGame;
    theGame = 0;

#ifdef LITE_VERSION
    if (storeLauncher) delete storeLauncher;
#endif
}




void MyGameWindow::initializeGL()
{
    qDebug() << "Initializing gl..";
    spriteBatch = new QtOpenGLSpriteBatch( width(), height(), this );
    glViewport(0,0, width(), height());

    if (theGame) delete theGame;
    theGame = new TheGame( spriteBatch );


        // Load the games basic textures.
    theGame->getGameTextures().playerTexture = glhelpLoadTexture( ":/images/player.png", false);
#ifdef Q_OS_SYMBIAN
    theGame->getGameTextures().bgTexture = glhelpLoadTexture( ":/images/bg_smaller.jpg", false);
#else
    theGame->getGameTextures().bgTexture = glhelpLoadTexture( ":/images/bg2.jpg", false);
#endif
    theGame->getGameTextures().fontTexture = glhelpLoadTexture( ":/images/font.png", true);
    theGame->getGameTextures().iconTexture = glhelpLoadTexture( ":/images/level.png", false);
    theGame->getGameTextures().slidersTexture = glhelpLoadTexture( ":/images/sliders.png",true);
#ifdef LITE_VERSION
    theGame->getGameTextures().logoTexture = glhelpLoadTexture( ":/images/logo_flag_mines_lite.png", true);
#else
    theGame->getGameTextures().logoTexture = glhelpLoadTexture( ":/images/logo_flag_mines.png", true);
#endif


    theGame->getGameTextures().levelTexture = createLevelTexture();



    glDisable( GL_CULL_FACE );
    glDisable( GL_DEPTH_TEST );
    glDepthMask( GL_FALSE );
}


/*!
    From GameWindow
    \see GameWindow.cpp
*/
void MyGameWindow::onDestroy()
{
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().bgTexture );
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().playerTexture );
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().fontTexture );
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().levelTexture );
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().iconTexture );
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().slidersTexture );
    glDeleteTextures(1, (GLuint*)&theGame->getGameTextures().logoTexture );


    if (spriteBatch) {
        delete spriteBatch;
        spriteBatch = 0;
    }

    if (theGame) delete theGame;
    theGame = 0;

}

bool MyGameWindow::eventFilter(QObject *object, QEvent *event) {
    // http://doc.trolltech.com/4.7/qevent.html#QEvent
    if (event->type() == QEvent::ActivationChange) {
        if (appExiting == false) {
            if (paused) paused=false; else paused=true;
            qDebug() << "Paused : " << paused;
            if (paused) timer->stop(); else timer->start();
            return false;
        }
    }
#ifdef MY_OS_MEEGO
    else if (event->type() == QEvent::WindowStateChange )
    {
        /*
        QWindowStateChangeEvent *change = static_cast<QWindowStateChangeEvent *>(event);

        Qt::WindowStates current = windowState();
        Qt::WindowStates old = change->oldState();
        //qDebug() << "widget is minimized in eventFilter?" << isMinimized();

        // did minimized flag change?
        if ((current ^ old) & Qt::WindowMinimized) {
            if (current & Qt::WindowMinimized) {
                // In MeeGo, the gl surface is somehow broken by the QMeegoGraphicsSystem.
                // This is why we must kill the timer, to prevent errors resulting gl painting.
                //qDebug() << "HIDING";
                killTimer(timerid);
                timerid = 0;
            }
        }
        return false;
        */
    }
#endif
    else {
        // Standard event processing
        return QObject::eventFilter(object, event);
    }
    return false;
}


#ifdef MS_USE_TOUCH_EVENTS
bool MyGameWindow::event(QEvent* event)
{
    switch (event->type()) {
    /*
    case QEvent::WindowActivate:
        qDebug("WindowActivate");
        //if(!splashScreen) startGame();
        break;
    case QEvent::WindowDeactivate:
        qDebug("WindowDeactivate");
        //if(!splashScreen) pauseGame();
        break;
*/
    case QEvent::TouchBegin:
    case QEvent::TouchUpdate:
    case QEvent::TouchEnd:
    {
        memcpy( exTouchEnabled, touchEnabled, sizeof(bool) * MAX_TOUCH_POINTS );
        QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(event)->touchPoints();
        int activePoints = touchPoints.count();
        bool previewMap = false;
        if (theGame && theGame->getLevel()) previewMap = theGame->getLevel()->isShowingMap();

        foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints)
        {
            int ind = touchPoint.id();

#ifdef Q_WS_MAEMO_6
            QPoint pte = touchPoint.pos().toPoint();
            QPoint p = QPoint( height() - pte.y(), pte.x());
#else
            QPoint p = touchPoint.pos().toPoint();
#endif
            touchPositions[ind][0] = p.x();
            touchPositions[ind][1] = p.y();

            switch (touchPoint.state()) {
            case Qt::TouchPointPressed:
            {
                /*
                if (previewMap) {
                    theGame->getLevel()->hideMap();
                    continue;
                }
                */
                if (theGame!=0 && ind==0 && activePoints<2)
                    theGame->press(  p.x(), p.y() );


                touchEnabled[ind] = true;
                continue;
            }
            case Qt::TouchPointMoved:
            {
                if (theGame!=0 && ind==0 && activePoints<2)
                    theGame->move(  p.x(), p.y() );

                touchEnabled[ind] = true;
                continue;
            }
            case Qt::TouchPointReleased:
            {
                if (theGame!=0 && ind==0 && activePoints<2)
                    theGame->release(  p.x(), p.y() );

                touchEnabled[ind] = false;
                continue;
            }
            default:
            case Qt::TouchPointStationary:
            {
                // The touch point did NOT move
                continue;
            }
            }
        }
        event->accept();

        // zoom in/out gesture..
        if (activePoints>1) {
            if (theGame) theGame->cancelPress();
            float collen = 0.0f;
            for (int f=1; f<activePoints; f++) {
                float dx = touchPositions[f][0] - touchPositions[0][0];
                float dy = touchPositions[f][1] - touchPositions[0][1];
                float l = sqrtf( dx*dx+dy*dy );
                collen += l;
            }

            if (lastTouchCount<2) {
                lastMultitouchDistance = collen;
            }


            if (theGame && (lastMultitouchDistance - collen) > 100.0f)
                theGame->getLevel()->showMap();


            if (theGame && (lastMultitouchDistance - collen) < -100.0f)
                theGame->getLevel()->hideMap();

        }



        lastTouchCount = activePoints;


        return true;
    }

    default:
        return QWidget::event(event);
    }

}

#else
void MyGameWindow::mousePressEvent(QMouseEvent *e) {
    if (theGame==0) return;
    theGame->press( e->x(), e->y() );
}

void MyGameWindow::mouseMoveEvent(QMouseEvent *e) {
    if (theGame==0) return;
     theGame->move( e->x(), e->y() );
}

void MyGameWindow::mouseReleaseEvent(QMouseEvent *e) {
    if (theGame==0) return;
    theGame->release( e->x(), e->y() );
}
#endif


void MyGameWindow::paintGL()
{

    QTime time = QTime::currentTime();
    int msecs = time.msecsTo( prevtime );
    prevtime = time;

    if (paused) {
        return;
    }
            // update here
    float frameDelta = fabs( (float)msecs/1000.0f );
    if (frameDelta>0.2f) frameDelta = 0.2f;
    if (theGame->update( frameDelta ) == false) {
        appExiting = true;
        qApp->exit(0);
    }


    glClearColor( 0, 0,0,0 );
    glClear( GL_COLOR_BUFFER_BIT );

    theGame->render();
    swapBuffers();

    if (theGame->launchOviClient == true ) {
#ifdef LITE_VERSION
        qDebug() << "Launching oviclient.";

#ifdef Q_OS_SYMBIAN
        if (!storeLauncher) storeLauncher = new QStoreLauncher();
        storeLauncher->LaunchNokiaStore(QString("http://store.nokia.mobi/content/253909"));     // full version of the flagmines
#else
        // Open with default browser
        QDesktopServices::openUrl(QUrl(QString("http://store.nokia.mobi/content/253909")));
#endif

        qDebug() << "done.";

#endif
        theGame->launchOviClient = false;



    }
}



/*!
  The size of our window has been changed. Set new projectionmatrix..
*/
void MyGameWindow::resizeGL(int w, int h)
{
    GLfloat mproj[16] = {
        1.0f, 0, 0, 0,
        0, (float)w / (float)h, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 1,
    };
    glViewport(0,0, w,h );
    memcpy( matrixProjection, mproj, sizeof( GLfloat ) * 16 );
    currentWidth = w;
    currentHeight = h;
    spriteBatch->setTargetSize( currentWidth, currentHeight );
    qDebug() << "ResizeGL: " << w << "," << h;
}






///////////////////////////////////////////////////////////// HELPERS

/*!
  Load an image with QImage, convert it to GL's format, create OpenGL texture and
  Return it's handle
*/
/*
GLuint MyGameWindow::glhelpLoadTexture( QString imageFile )
{
    QImage image = QImage( imageFile );
    if (image.width()<1 || image.height()<1) return 0;      // Failed to load the texture
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLuint* pTexData = new GLuint[image.width() * image.height()];
    GLuint* sdata = (GLuint*)image.bits();
    GLuint* tdata = pTexData;

    for (int y=0; y<image.height(); y++)
        for (int x=0; x<image.width(); x++) {
            *tdata = ((*sdata&255) << 16) | (((*sdata>>8)&255) << 8)
                    | (((*sdata>>16)&255) << 0) | (((*sdata>>24)&255) << 24);

            sdata++;
            tdata++;
    };

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pTexData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    delete [] pTexData;
    return texture;
}
*/
GLuint MyGameWindow::glhelpLoadTexture( QString name, bool mipmaps )
{
    GLuint handle = 0;
    QImage texture;
    if(texture.load(name))
    {
        // bindTexture has flipped y with default bind options
        if (mipmaps)
            handle=bindTexture(texture, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption);
        else
            handle=bindTexture(texture, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
    }
    else
    {
        qDebug("load texture failed");
    }
    qDebug("loadGLTexture ret %d", handle);
    return handle;
}




unsigned int getpixel( QImage &i, int x, int y) {
    if (x<0) x+=i.width();
    if (y<0) y+=i.height();
    if (x>=i.width()) x-=i.width();
    if (y>=i.height()) y-=i.height();

    unsigned int col =  ((unsigned int*)(i.bits()))[ y*(i.bytesPerLine()>>2) +x ];

    //return (col&0xFF00ff00) | ((col&255)<<16) | ((col>>16)&255);
    return col;
}

unsigned int blendColors( unsigned int c1, unsigned int c2, int mul ) {
    if (mul<0) mul = 0;
    if (mul>256) mul = 256;
    int r = ((c1&255)*mul + (c2&255) *(256-mul))>>8;
    int g = (((c1>>8)&255)*mul + ((c2>>8)&255) * (256-mul))>>8;
    int b = (((c1>>16)&255)*mul + ((c2>>16)&255) * (256-mul))>>8;
    if (r>255) r=255;
    if (g>255) g=255;
    if (b>255) b=255;
    return ((r) | (g<<8) | (b<<16));
}



    // creates a level texture from the two basic textures
int MyGameWindow::createLevelTexture() {
    QImage opened = QImage(":/images/opened.png");
    QImage unopened = QImage(":/images/unopened.png");
    QImage unopened_overlay = QImage(":/images/unopened_overlay.png");

    int gw = opened.width();
    int gh = opened.height();

    // Create 4x8 texture
    int targetWidth = gw*4;
    int targetHeight = gh*8;

    GLuint* pTexData = new GLuint[targetWidth*targetHeight];

    int targetPos = 0;

    unsigned int c1,c2,c3;

    float m1,m2,m3,m4, mc;


    for (int y=0; y<gh; y++) {
        float yp = ((float)y/(float)(gh-1) - 0.5f)*2.0f;
        //yp=yp*fabs(yp);
        for (int x=0; x<gw; x++) {
            float xp = ((float)x/(float)(gw-1) - 0.5f)*2.0f;
            //xp = xp*fabs(xp);


            mc = 1.0f - (xp*xp+yp*yp); //dto(xp,yp,0.0f, 0.0f);
            if (mc<0.0f) mc = 0.0f;

            m1 = (1.0f-xp)/2.0f * ((1.0f-yp)/2.0f);
            m2 = (1.0f+xp)/2.0f * ((1.0f-yp)/2.0f);
            m4 = (1.0f-xp)/2.0f * ((1.0f+yp)/2.0f);
            m3 = (1.0f+xp)/2.0f * ((1.0f+yp)/2.0f);
            mc*=3;
                // normalize
            float ftemp = sqrtf( mc*mc + m1*m1 +  m2*m2 + m3*m3  + m4*m4 );
            mc /= ftemp;
            m1 /= ftemp;
            m2 /= ftemp;
            m3 /= ftemp;
            m4 /= ftemp;

            c1 = getpixel(opened,x,y);
            c2 = getpixel(unopened,x,y);
            c3 = getpixel(unopened_overlay, x,y);


            float tvals[2];
            int tindex=0;
            int blocky, blockx;
            for (int center=0; center<2; center++)
                for (int ul=0; ul<2; ul++)
                    for (int ur=0; ur<2; ur++)
                        for (int dr=0; dr<2; dr++)
                            for (int dl=0; dl<2; dl++)
                            {
                                tvals[0] = 0.0f;
                                tvals[1] = 0.0f;
                                tvals[ul] += m1;
                                tvals[ur] += m2;
                                tvals[dr] += m3;
                                tvals[dl] += m4;
                                tvals[center] += mc;

                                blocky = tindex/4;
                                blockx = tindex-blocky*4;
                                // Pixel for this texture is known
                                if (center==1) {
                                    pTexData[(y+blocky*gh)*targetWidth+x+blockx*gw] = 0xFF000000 |
                                            blendColors(
                                                blendColors(c2,c1, 128.0f - tvals[0]*200.0f + tvals[1] * 200.0f),
                                                c3,
                                                256-(c3>>24) );
                                } else {
                                    pTexData[(y+blocky*gh)*targetWidth+x+blockx*gw] = 0xFF000000 |
                                                blendColors(c2,c1, 128.0f - tvals[0]*200.0f + tvals[1] * 200.0f);
                                }


                                tindex++;
                            }



        }
    }

    // bindTexture has flipped y with default bind options
    QImage texTemp = QImage((uchar*)pTexData, targetWidth, targetHeight,targetWidth*4, QImage::Format_ARGB32 );
    GLuint handle=bindTexture(texTemp, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption);
    delete [] pTexData;
    return handle;

/*
    GLuint texture;         // return value
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, targetWidth, targetHeight, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pTexData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    delete [] pTexData;
    return texture;
*/


}
